package gt

import (
	"gitee.com/dark.H/gs"
	"github.com/mattn/go-runewidth"
	termbox "github.com/nsf/termbox-go"
)

const (
	MODE_LIST = 0
	MODE_RAW  = 1
)

type AppPanel interface {
	Draw()
	OnKey(k termbox.Event) bool
	ExtendKey(int, func(key, cursor int, now any) bool)
}

type L struct {
	Row   int
	Col   int
	Width int
}

type PanelLoc struct {
	X          int
	Y          int
	Xpos       int
	Ypos       int
	maxRow     int
	maxWidth   int
	ShowBar    bool
	ShowBorder bool
	HideCursor bool
	Mode       int
	locStr     gs.Str
	TItle      gs.Str
}

func (loc *PanelLoc) Copy() *PanelLoc {
	nl := &PanelLoc{
		X:          loc.X,
		Y:          loc.Y,
		Xpos:       loc.Xpos,
		Ypos:       loc.Ypos,
		maxRow:     loc.maxRow,
		maxWidth:   loc.maxWidth,
		ShowBar:    loc.ShowBar,
		ShowBorder: loc.ShowBorder,
		HideCursor: loc.HideCursor,
		Mode:       loc.Mode,
		locStr:     loc.locStr,
		TItle:      loc.TItle,
	}
	return nl
}

func (loc *PanelLoc) Label(some gs.Str, lineColumn ...int) {
	oldX, oldY := loc.X, loc.Y

	some = some.Replace("\n", "\\n").Replace("\r", "\\r")
	if lineColumn == nil {
		loc.Cusor().A(some).Print(true)
		loc.X = oldX
		loc.Y = oldY

	} else {
		line := lineColumn[0]
		column := loc.X
		if len(lineColumn) > 1 {
			column = lineColumn[1]
		}
		loc.Cusor(line, column).A(some).Print(true)
		loc.X = oldX
		loc.Y = oldY

	}
}

func (loc *PanelLoc) Cusor(rowColumn ...int) *PanelLoc {
	if loc.maxWidth == 0 {
		loc.maxRow, loc.maxWidth = gs.GetWindowsSize()
	}
	if loc.Y < 0 {
		loc.Y += loc.maxRow
	}
	if rowColumn == nil {
		// fmt.Println(loc.Y, loc.X)
		// time.Sleep(1 * time.Second)

		loc.locStr = loc.locStr.ANSICursor(loc.Y, loc.X)
	} else {
		row := rowColumn[0]
		col := loc.X
		if len(rowColumn) > 1 {
			col = rowColumn[1]
		}
		if row < 0 {
			row += loc.maxRow
		}
		if col < 0 {
			col += loc.maxWidth
		}
		loc.locStr = loc.locStr.ANSICursor(row, col)
	}
	return loc
}

func (loc *PanelLoc) A(s gs.Str) *PanelLoc {
	loc.locStr += gs.Str(s)
	return loc
}

func (loc *PanelLoc) HiddenCursor() *PanelLoc {
	loc.locStr += "\x1b[?25l"
	return loc
}
func (loc *PanelLoc) ShowCursor() *PanelLoc {
	loc.locStr += "\x1b[?25h"
	return loc
}

func (loc *PanelLoc) Line(i int) *PanelLoc {
	if loc.maxWidth == 0 {
		loc.maxRow, loc.maxWidth = gs.GetWindowsSize()
	}
	if i < 0 {
		i = i + loc.maxRow
	}
	loc.locStr = loc.locStr.ANSICursor(loc.Y+i, loc.X)
	return loc
}

func (loc *PanelLoc) Column(i int) *PanelLoc {
	if loc.maxWidth == 0 {
		loc.maxRow, loc.maxWidth = gs.GetWindowsSize()
	}
	if i < 0 {
		i = i + loc.maxWidth
	}
	loc.locStr = loc.locStr.ANSICursor(loc.Y, loc.X+i)
	return loc
}

func (loc *PanelLoc) Reset() *PanelLoc {
	if loc.maxWidth == 0 {
		loc.maxRow, loc.maxWidth = gs.GetWindowsSize()
	}
	if loc.Y < 0 {
		loc.Y = loc.Y + loc.maxRow
	}

	loc.locStr += loc.locStr.ANSI("H")

	return loc
}

func (loc *PanelLoc) String() string {
	return loc.locStr.Str()
}

func (loc *PanelLoc) Str() gs.Str {
	return loc.locStr
}

func (loc *PanelLoc) Print(resetStr ...bool) string {
	loc.locStr.Print()
	e := loc.locStr.Str()
	if resetStr != nil && resetStr[0] {
		loc.locStr = gs.Str("")
	}
	return e
}

func (loc *PanelLoc) Printline(resetStr ...bool) string {
	gs.Str("").ANSIClearThisLine().Add(loc.locStr).Print()
	e := loc.locStr.Str()
	if resetStr != nil && resetStr[0] {
		loc.locStr = gs.Str("")
	}
	return e
}

func (loc *PanelLoc) Tip(msg gs.Str) *PanelLoc {

	_, c := gs.GetWindowsSize()
	if msg.Len() >= c-6 {
		msg = msg[:c-6]
	} else {
		msg += gs.Str(" ").Repeat(c - 6 - msg.Len())
	}
	L{
		Row:   -1,
		Col:   1,
		Width: c,
	}.Print(msg.ANSISelected())
	return loc
}

func (loc *PanelLoc) PrintProgressBar(linenum, windowsheight int) *PanelLoc {

	bar_start := 0
	bar_end := 0
	windowsheight -= 1
	bar := int(windowsheight * windowsheight / linenum)
	bar_start = int(loc.Ypos * windowsheight / linenum)
	bar_end = bar_start + bar

	if linenum < windowsheight {
		bar = windowsheight
		bar_start = 0
		bar_end = windowsheight
	}
	pre := ""
	con := gs.Str("")
	for i := 0; i <= windowsheight; i++ {
		if i >= bar_start && i <= bar_end {
			if i == bar_start {
				pre = "╦"
			} else if i == bar_end {
				pre = "╩"
			} else {
				pre = "║"
			}
			con += L{
				Row:   loc.Y + i + 1,
				Col:   loc.X,
				Width: 3,
			}.Sprint(gs.Str(pre))
		}
	}
	con.Print()
	return loc
}

func (loc *PanelLoc) PrintInArea(multiline gs.Str, height, width int, selectedNo ...int) {

	maxheight, maxwidth := gs.GetWindowsSize()
	maxheight += 1
	if width == 0 {
		height, width = gs.GetWindowsSize()
	}
	if loc.Y+height > maxheight {
		height = maxheight - loc.Y
		if height < 0 {
			height = 0
		}
	}
	if loc.X+width > maxwidth {
		width = maxwidth - loc.X
		if width < 0 {
			width = 0
		}
	}

	contentHeiht := height
	contentWidth := width
	if contentHeiht == 1 && loc.ShowBorder {
		loc.ShowBorder = false
	}
	if loc.ShowBorder {
		loc.PrintBorder(height, width)
		contentHeiht = height - 2
		contentWidth = width - 2
	}
	if selectedNo == nil {
		multiline = gs.Str(runewidth.Wrap(multiline.Str(), contentWidth))

	}

	if loc.ShowBar {
		loc.PrintProgressBar(multiline.Count("\n"), contentHeiht)
	}

	if contentHeiht < 0 || contentWidth < 0 {
		return
	}

	X := loc.X
	Y := loc.Y
	if loc.ShowBorder {
		X = loc.X + 1
		Y = loc.Y + 1
	}
	end := gs.Str("")

	draw_num := 0

	var lastPoint L
	multiline.EveryLine(func(lineno int, line gs.Str) {
		lineno -= loc.Ypos
		if lineno < 0 {
			return
		}
		if lineno >= contentHeiht {
			return
		}
		// hasBar := false
		col := X

		if loc.Xpos >= line.Len() {
			line = gs.Str("")
		} else {
			line = line[loc.Xpos:]
		}
		lastPoint = L{
			Row:   Y + lineno,
			Col:   col,
			Width: contentWidth,
		}
		if selectedNo != nil && selectedNo[0] == draw_num {
			end += lastPoint.Sprint(line, true)
		} else {
			end += lastPoint.Sprint(line)
		}

		draw_num += 1
	})

	end.Print()
}

func (loc *PanelLoc) ClearInArea(height, width int, special_c ...rune) {
	maxheight, maxwidth := gs.GetWindowsSize()
	maxheight += 1
	if width == 0 {
		height, width = gs.GetWindowsSize()
	}
	if loc.Y+height > maxheight {
		height = maxheight - loc.Y
		if height < 0 {
			height = 0
		}
	}
	if loc.X+width > maxwidth {
		width = maxwidth - loc.X
		if width < 0 {
			width = 0
		}
	}
	// loc.PrintCorner(height, width)
	contentHeiht := height
	contentWidth := width
	X := loc.X
	Y := loc.Y

	line := gs.Str(" ").Repeat(contentWidth)
	if special_c != nil {
		line = gs.Str(special_c).Repeat(contentWidth)
	}
	multiline := (line + "\n").Repeat(contentHeiht)
	end := gs.Str("")
	// if loc.HideCursor {
	// 	end = end.ANSIHideCursor()
	// } else {
	// 	end = end.ANSIShowCursor()
	// }

	multiline.EveryLine(func(lineno int, line gs.Str) {
		if lineno >= contentHeiht {
			return
		}
		end += L{
			Row:   Y + lineno,
			Col:   X,
			Width: contentWidth,
		}.Sprint(line)
	})
	// if loc.HideCursor {
	// 	end.ANSIHideCursor().Print()
	// } else {
	// 	end.ANSIShowCursor().Print()
	// }
	end.Print()
}

func (loc *PanelLoc) PrintLineVertical(x, y, width int) gs.Str {
	// rt := "┐"
	// rb := "┘"
	// lt := "┌"
	// l := "│"
	// lb := "└"
	h := "─"
	X := x
	Y := y
	m := gs.Str("")
	for off := 0; off < width; off++ {
		// if off == 0 {
		m += L{
			Row:   Y,
			Col:   X + off,
			Width: 1,
		}.Sprint(gs.Str(h))
		// }
	}
	return m
}

func (loc *PanelLoc) PrintLineHorizon(x, y, height int) gs.Str {
	// rt := "┐"
	// rb := "┘"
	// lt := "┌"
	l := "│"
	// lb := "└"
	// h := ""
	X := x
	Y := y
	m := gs.Str("")
	for off := 0; off < height; off++ {
		// if off == 0 {
		m += L{
			Row:   Y + off,
			Col:   X,
			Width: 1,
		}.Sprint(gs.Str(l))
		// }
	}
	return m
}

func (loc *PanelLoc) PrintDotCorner(width, height int) gs.Str {
	rt := "┐"
	rb := "┘"
	lt := "┌"
	lb := "└"
	// lt := ""
	// lb := ""
	// h := ""

	X := loc.X
	Y := loc.Y
	m := gs.Str("")
	m += L{
		Row:   Y,
		Col:   X,
		Width: 1,
	}.Sprint(gs.Str(lt))

	if loc.TItle.Len() > 0 {
		title := loc.TItle.Replace("\t", " ")
		// if width-6 < 0 {
		// 	width = 7
		// }
		if title.Len() > width-6 {
			title = title[:width-6]
		}
		m += title.Color("g")
	}

	m += L{
		Row:   Y + height,
		Col:   X,
		Width: 1,
	}.Sprint(gs.Str(lb))

	m += L{
		Row:   Y,
		Col:   X + width,
		Width: 2,
	}.Sprint(gs.Str(rt))
	m += L{
		Row:   Y + height,
		Col:   X + width,
		Width: 1,
	}.Sprint(gs.Str(rb))

	return m
}

func (loc *PanelLoc) PrintBorder(heiht, width int) *PanelLoc {
	if heiht < 2 || width < 2 {
		return loc
	}
	m := gs.Str("")
	m += loc.PrintLineVertical(loc.X, loc.Y, width)
	m += loc.PrintLineVertical(loc.X, loc.Y+heiht-1, width)
	if !loc.ShowBar {
		m += loc.PrintLineHorizon(loc.X, loc.Y+1, heiht-2)

	}
	m += loc.PrintLineHorizon(loc.X+width-1, loc.Y+1, heiht-2)
	m += loc.PrintDotCorner(width-1, heiht-1)
	m.Print()
	return loc
}

func (loc *PanelLoc) PrintCorner(heiht, width int) {
	lt := gs.Str("╒═")
	rt := gs.Str("═╕")
	rb := gs.Str("─┘")
	lb := gs.Str("└─")
	X := loc.X
	Y := loc.Y
	m := L{
		Row:   Y,
		Col:   X,
		Width: 6,
	}.Sprint(lt)
	if loc.TItle.Len() > 0 {
		title := loc.TItle.Replace("\t", " ")
		if title.Len() > width-3 {
			title = title[:width-3]

		}
		m += " " + title.Color("B", "g")
	}
	m += L{
		Row:   Y + 1,
		Col:   X,
		Width: 3,
	}.Sprint("│")

	m += L{
		Row:   Y,
		Col:   X + width - 2,
		Width: 6,
	}.Sprint(rt)
	m += L{
		Row:   Y + 1,
		Col:   X + width - 1,
		Width: 3,
	}.Sprint("│")

	m += L{
		Row:   Y + heiht - 2,
		Col:   X,
		Width: 3,
	}.Sprint("│")
	m += L{
		Row:   Y + heiht - 1,
		Col:   X,
		Width: 6,
	}.Sprint(lb)

	m += L{
		Row:   Y + heiht - 2,
		Col:   X + width - 1,
		Width: 3,
	}.Sprint("│")
	m += L{
		Row:   Y + heiht - 1,
		Col:   X + width - 2,
		Width: 6,
	}.Sprint(rb)
	m.Print()
}

func ShowLabel(msg gs.Str) {
	_, c := gs.GetWindowsSize()
	if msg.Len() >= c-6 {
		msg = msg[:c-6]
	} else {
		msg += gs.Str(" ").Repeat(c - 6 - msg.Len())
	}
	L{
		Row:   -1,
		Col:   1,
		Width: c,
	}.Print(msg.ANSISelected())
}
